"""
交换机定义为网络
"""

import IPy
from flask import current_app
from flask_restx import abort, reqparse
from openstack import exceptions

from app.api.v1.organizer.rangeVm import VmBase
from app.models import rangeModel, switchModel
from configs.settings import flask_config
from tools import db, redis_conn


class SwitchNetwork(VmBase):
    def post(self):
        parser = reqparse.RequestParser()
        parser.add_argument("range_uuid", type=str, help="UUID不能为空", required=True)
        parser.add_argument("cidr", type=str, required=True)
        args = parser.parse_args()

        try:
            switch = switchModel.ActiveSwitchs.query.filter_by(
                range_uuid=args["range_uuid"], cidr=args["cidr"]
            ).first()
            network_id = self.createNetwork(range_uuid=args["range_uuid"])

            if not switch:
                subnet = self.conn.network.create_subnet(
                    network_id=network_id,
                    cidr=args["cidr"],
                    ip_version="4",
                )
                # port = self.conn.create_port(
                #     network_id, fixed_ips=[{"subnet_id": subnet.id}]
                # )

                obj = switchModel.ActiveSwitchs(
                    range_uuid=args["range_uuid"],
                    subnet_id=subnet.id,
                    cidr=args["cidr"],
                    network_id=network_id,
                )
                switchModel.db.session.add(obj)
                switchModel.db.session.commit()

                # port = switchModel.Ports(
                #     port_id=port.id,
                #     range_uuid=args["range_uuid"],
                #     ip=port.fixed_ips[0]["ip_address"],
                #     network_id=network_id,
                #     subnet_id=subnet.id,
                # )
                # switchModel.db.session.add(port)
                # switchModel.db.session.flush()
            else:
                abort(500, message="该网段(%s)以存在请重新选择网段" % args["cidr"])
            return {
                "code": 200,
                "data": {"subnet_id": obj.subnet_id, "switch_id": obj.id},
            }
        except Exception as e:
            db.session().rollback()
            current_app.logger.exception(e)
            abort(500, message="交换机创建失败")

    def get(self, range_uuid, subnet_id):

        if range_uuid:
            if subnet_id == "no":
                switchs = switchModel.ActiveSwitchs.query.filter_by(
                    range_uuid=range_uuid
                ).all()
                cidr = []
                all_cidr = flask_config.NETWORK["CIDRS"]
                for switch in switchs:
                    cidr.append(switch.cidr)
                no_use_cidr = list(set(all_cidr) - set(cidr))
                info = {"use_cidr": cidr, "no_use_cidr": no_use_cidr}
                return {"code": 200, "data": info}
            else:
                switch = switchModel.ActiveSwitchs.query.filter_by(
                    range_uuid=range_uuid, subnet_id=subnet_id
                ).first()
                info = {}
                if switch:
                    info["swich_id"] = switch.id
                    info["cidr"] = switch.cidr
                    info["subnet_id"] = switch.subnet_id
                return {"code": 200, "data": info}
        else:
            abort(400, message="靶场id不可为空")

    def delete_switch(self, args):

        switch = switchModel.ActiveSwitchs.query.filter_by(
            range_uuid=args["range_uuid"], subnet_id=args["subnet_id"]
        ).first()

        # 判断交换机绑定的虚拟机
        vm_list = rangeModel.RangeVm.query.filter_by(subnet_id=args["subnet_id"]).all()
        if vm_list:
            abort(400, message="该交换机有连接的虚拟机，不可直接删除，请先解绑！！！")

        route_list = switchModel.ActiveRoutes.query.filter_by(
            range_uuid=args["range_uuid"]
        ).all()
        # 解除和该交换机连接的路由器
        for router in route_list:
            if router.binding_switchs:
                subnet_list = router.binding_switchs.split(",")
                if str(switch.id) in subnet_list:
                    self.conn.network.remove_interface_from_router(
                        router.op_route, switch.subnet_id
                    )
                    index = subnet_list.index(str(switch.id))
                    subnet_list.pop(index)
                    new_binding_switchs = ",".join(subnet_list)
                    switchModel.ActiveRoutes.query.filter_by(
                        range_uuid=args["range_uuid"]
                    ).update({"binding_switchs": new_binding_switchs})
                    switchModel.db.session.commit()
                    break
        # 删除子网
        port_list = switchModel.ActivePorts.query.filter_by(
            subnet_id=switch.subnet_id
        ).all()
        for port in port_list:
            if port.ip in IPy.IP(switch.cidr):
                self.conn.network.delete_port(port.port_id)
                db.session.delete(port)
        db.session.commit()
        self.conn.network.delete_subnet(switch.subnet_id)
        switchModel.db.session.delete(switch)
        switchModel.db.session.commit()

    def delete(self):
        parser = reqparse.RequestParser()
        parser.add_argument("range_uuid", help="靶场信息有误", required=True)
        parser.add_argument("subnet_id", help="靶场信息有误", required=True)
        args = parser.parse_args()

        if not all([args["range_uuid"], args["subnet_id"]]):
            abort(400, message="参数错误")
        else:
            self.delete_switch(args)
            return {"code": 200, "data": "删除成功"}


class SubnetPort(VmBase):
    def post(self):
        # number、ip不可同时存在
        parser = reqparse.RequestParser()
        parser.add_argument("range_uuid", help="靶场信息有误", required=True)
        parser.add_argument("subnet_id", help="交换机信息有误", required=True)
        parser.add_argument("number")
        parser.add_argument("ip")
        args = parser.parse_args()

        number = args.get("number")
        if not number:
            number = "1"
        ip = args.get("ip")
        # number、ip不可同时存在
        if int(number) > 1:
            if ip:
                abort(400, message="创建多个端口时，不能指定ip")
        # 去掉dhcp，gw
        if ip and ip.split(".")[-1] in ["1", "2"]:
            abort(400, message="该ip地址不可使用")

        switch = switchModel.ActiveSwitchs.query.filter_by(
            range_uuid=args["range_uuid"], subnet_id=args["subnet_id"]
        ).first()
        if switch:
            if ip and (IPy.IP(ip) not in list(IPy.IP(switch.cidr))):
                abort(400, message="指定ip地址不在该交换机的网段内")
            port_list = []
            for _ in range(int(number)):
                try:
                    if not ip:
                        port = self.conn.create_port(
                            switch.network_id,
                            fixed_ips=[{"subnet_id": switch.subnet_id}],
                        )
                    else:
                        port = self.conn.create_port(
                            switch.network_id,
                            fixed_ips=[
                                {"ip_address": ip, "subnet_id": switch.subnet_id}
                            ],
                        )

                except exceptions.ConflictException as e:
                    current_app.logger.exception(e)
                    abort(400, message="指定ip地址已存在")

                port_info = {
                    "port_id": port.id,
                    "ip": port["fixed_ips"][0]["ip_address"],
                }
                port_list.append(port_info)

                # 记录未绑定的port，以供开机时使用
                port = switchModel.ActivePorts(
                    port_id=port.id,
                    range_uuid=args["range_uuid"],
                    ip=port.fixed_ips[0]["ip_address"],
                    network_id=switch.network_id,
                    subnet_id=switch.subnet_id,
                )
                switchModel.db.session.add(port)
                switchModel.db.session.flush()

            return {"code": 200, "data": port_list}


class VmPortBinding(VmBase):
    def get(self, range_uuid, vm_uuid, subnet_id):
        # 锁防连续多次触发
        with redis_conn.lock(
            f"ORDER_VM_PORT_BINDING_CHECK::{range_uuid}::{vm_uuid}::{subnet_id}",
            timeout=10,
        ):
            # 清除当前线程db(同一个接口连续多次请求)中所有对象缓存及事务，及事务隔离影响，以获取最新数据库信息
            return self.vm_port_bind(range_uuid, vm_uuid, subnet_id)

    def vm_port_bind(self, range_uuid, vm_uuid, subnet_id):
        if subnet_id == "null":
            return {"code": 200, "data": []}
        vm_info = rangeModel.RangeVm.query.filter_by(
            range_uuid=range_uuid, vm_uuid=vm_uuid
        ).first()
        ip_list = (
            rangeModel.RangeVm.query.with_entities(rangeModel.RangeVm.v4_fixed_ip)
            .distinct()
            .filter_by(range_uuid=range_uuid)
            .filter(rangeModel.RangeVm.v4_fixed_ip.isnot(None))
            .all()
        )
        if not vm_info:
            abort(400, message="获取虚拟机信息错误")

        if vm_info.v4_fixed_ip:
            port = switchModel.ActivePorts.query.filter_by(
                subnet_id=subnet_id, ip=vm_info.v4_fixed_ip
            ).first()
            if port:
                return {
                    "code": 200,
                    "data": [{"port_id": port.port_id, "ip": vm_info.v4_fixed_ip}],
                }
            try:
                switch = switchModel.ActiveSwitchs.query.filter_by(
                    subnet_id=vm_info.subnet_id
                ).first()
                port_op = self.conn.create_port(
                    switch.network_id,
                    fixed_ips=[
                        {
                            "ip_address": vm_info.v4_fixed_ip,
                            "subnet_id": switch.subnet_id,
                        }
                    ],
                )
                port_ob = switchModel.ActivePorts(
                    port_id=port_op.id,
                    range_uuid=range_uuid,
                    ip=port_op.fixed_ips[0]["ip_address"],
                    network_id=switch.network_id,
                    subnet_id=switch.subnet_id,
                )
                db.session.add(port_ob)
                db.session.commit()
                return {
                    "code": 200,
                    "data": [
                        {"port_id": port_ob.port_id, "ip": vm_info.v4_fixed_ip}
                    ],
                }
            except exceptions.ConflictException as e:
                abort(400, message=e)
        port_list = []
        switch = switchModel.ActiveSwitchs.query.filter_by(subnet_id=subnet_id).first()
        if vm_info.subnet_id != switch.subnet_id:
            abort(400, message="交换机不是该虚拟机相连的交换机")
        port_db_list = (
            switchModel.ActivePorts.query.filter_by(
                range_uuid=range_uuid, subnet_id=switch.subnet_id, device_id=None
            )
            .order_by(switchModel.ActivePorts.ip)
            .all()
        )
        for port in port_db_list:
            port_info = {"port_id": port.port_id, "ip": port.ip}
            if any(port.ip in ip for ip in ip_list):
                continue
            port_list.append(port_info)
        return {"code": 200, "data": port_list}


class Router(VmBase):
    def delete_route(self, range_uuid, router_id):
        if not all([range_uuid, router_id]):
            abort(400, message="参数错误")
        router = switchModel.ActiveRoutes.query.filter_by(op_route=router_id).first()

        if router:
            if router.binding_switchs:
                abort(400, message="该路由器存在绑定的交换机，请先解除绑定")
            else:
                db.session.delete(router)
                db.session.commit()
        else:
            abort(400, message="该路由器不存在")

        try:
            router_tables = switchModel.ActiveRouterTable.query.filter_by(
                router_id=router.op_route
            ).all()
            self.conn.update_router(router.op_route, routes=[])
            for table in router_tables:
                switchModel.db.session.delete(table)
            db.session.commit()
            self.conn.network.delete_router(router_id)
        except Exception as e:
            current_app.logger.exception(e)
            abort(400, message="删除失败，请查看日志")

    def post(self):
        parser = reqparse.RequestParser()
        parser.add_argument("range_uuid", help="靶场信息有误", required=True)
        args = parser.parse_args()

        range_uuid = args["range_uuid"]
        if not range_uuid:
            abort(400, message="参数错误")

        router_name = range_uuid[:5] + "-" + "router"
        router = self.conn.network.create_router(name=router_name)
        print(router)

        obj = switchModel.ActiveRoutes(op_route=router.id, range_uuid=range_uuid)
        db.session.add(obj)
        db.session.commit()
        return {"code": 200, "data": {"router_id": obj.op_route}}

    def delete(self):
        parser = reqparse.RequestParser()
        parser.add_argument("range_uuid", help="靶场信息有误", required=True)
        parser.add_argument("router_id", help="路由器信息有误", required=True)
        args = parser.parse_args()

        range_uuid = args["range_uuid"]
        router_id = args["router_id"]
        self.delete_route(range_uuid, router_id)
        return {"code": 200, "data": "删除成功"}

    def get(self, router_id):
        gw_ip_list = switchModel.ActivePorts.query.filter_by(device_id=router_id).all()
        router_gw = []
        for port in gw_ip_list:
            router_gw.append(port.ip)
        return {"code": 200, "data": {"gw_ip": router_gw}}


class SwitchCidr(VmBase):
    def get(self, range_uuid):
        switch_list = switchModel.ActiveSwitchs.query.filter_by(
            range_uuid=range_uuid
        ).all()
        cidr_list = flask_config.NETWORK["CIDRS"]

        for switch in switch_list:
            if switch.cidr:
                index = cidr_list.index(switch.cidr)
                cidr_list.pop(index)
        return {"code": 200, "data": cidr_list}


class RouterTables(VmBase):
    def post(self):
        parser = reqparse.RequestParser()
        parser.add_argument("router_id", help="路由器信息有误", required=True)
        parser.add_argument("destination_cidr", help="目的cidr信息有误", required=True)
        parser.add_argument("next_hop", help="下一跳信息有误", required=True)
        args = parser.parse_args()

        router = switchModel.ActiveRoutes.query.filter_by(
            op_route=args["router_id"]
        ).first()
        next_hop = False
        switch_cidr_list = []

        if router:
            switch_str = router.binding_switchs
            if switch_str:
                switch_list = switch_str.split(",")
                for switch_id in switch_list:
                    switch = switchModel.ActiveSwitchs.query.filter_by(
                        id=int(switch_id)
                    ).first()
                    if switch:
                        switch_cidr_list.append(switch.cidr)

        if "/" in args["next_hop"]:
            abort(400, message="输入的下一跳不合法")
        try:
            IPy.IP(args["destination_cidr"])
        except ValueError:
            abort(400, message="输入的目的cidr不合法")
        try:
            for cidr in switch_cidr_list:
                if args["next_hop"] in IPy.IP(cidr):
                    next_hop = True
                    break
        except ValueError:
            abort(400, message="输入的下一跳不合法")
        if not next_hop:
            abort(400, message="输入的下一跳不在该路由器连接的子网所在的网段内")

        routes = []
        router_table_list = switchModel.ActiveRouterTable.query.filter_by(
            router_id=router.op_route
        ).all()
        for route_table in router_table_list:
            static_route = {
                "destination": route_table.destination_cidr,
                "nexthop": route_table.next_hop,
            }
            routes.append(static_route)

        routes.append(
            {"destination": args["destination_cidr"], "nexthop": args["next_hop"]}
        )
        try:
            self.conn.update_router(router.op_route, routes=routes)
        except exceptions.BadRequestException as e:
            current_app.logger.error("静态路由表创建失败", exc_info=e)
            abort(400, message=f"静态路由表创建失败: {e.message}")
        obj = switchModel.ActiveRouterTable(
            router_id=args["router_id"],
            destination_cidr=args["destination_cidr"],
            next_hop=args["next_hop"],
        )
        db.session.add(obj)
        db.session.commit()
        return {"code": 200, "data": {"route_table_id": obj.id}}

    def get(self, router_id):
        routes = []
        router_table_list = switchModel.ActiveRouterTable.query.filter_by(
            router_id=router_id
        ).all()
        for route_table in router_table_list:
            static_route = {
                "id": route_table.id,
                "destination": route_table.destination_cidr,
                "nexthop": route_table.next_hop,
            }
            routes.append(static_route)

        return {"code": 200, "data": routes}

    def delete(self):
        parser = reqparse.RequestParser()
        parser.add_argument("route_table_id", help="路由器信息有误", required=True)
        parser.add_argument("router_id", help="路由器信息有误", required=True)
        args = parser.parse_args()

        router_table_ob = switchModel.ActiveRouterTable.query.filter_by(
            id=args["route_table_id"]
        ).first()
        if not router_table_ob:
            abort(400, message="该路由表不存在")
        routes = []
        router_table_list = switchModel.ActiveRouterTable.query.filter_by(
            router_id=args["router_id"]
        ).all()
        for route_table in router_table_list:
            static_route = {
                "destination": route_table.destination_cidr,
                "nexthop": route_table.next_hop,
            }
            routes.append(static_route)
        index = routes.index(
            {
                "destination": router_table_ob.destination_cidr,
                "nexthop": router_table_ob.next_hop,
            }
        )

        routes.pop(index)
        self.conn.update_router(args["router_id"], routes=routes)

        db.session.delete(router_table_ob)
        db.session.commit()
        return {"code": 200, "info": "删除成功"}
